Implemented GtkExtendedLayout on GtkComboBox.
authorTristan Van Berkom <tristan.van.berkom@gmail.com>
Thu, 8 Apr 2010 22:54:47 +0000 (18:54 -0400)
committerTristan Van Berkom <tristan.van.berkom@gmail.com>
Thu, 8 Apr 2010 22:54:47 +0000 (18:54 -0400)
With this commit it is possible to use ellipsizing text
in the combobox's cell renderers and have them desire to
expand to natural size when placed in a GtkBox.

gtk/gtkcombobox.c

index fd92dd0d5d52e114fb5c7f41ac54598fd620798f..6b13f50ad808447887697f68abc5901fc01662da 100644 (file)
@@ -38,6 +38,7 @@
 #include "gtktreeselection.h"
 #include "gtkvseparator.h"
 #include "gtkwindow.h"
+#include "gtkextendedlayout.h"
 #include "gtkprivate.h"
 
 #include <gdk/gdkkeysyms.h>
@@ -109,8 +110,9 @@ struct _GtkComboBoxPrivate
   guint scroll_timer;
   guint resize_idle_id;
 
-  gint width;
-  gint height;
+  GtkRequisition minimum_size;
+  GtkRequisition natural_size;
+
   GSList *cells;
 
   guint popup_in_progress : 1;
@@ -277,8 +279,6 @@ static void     gtk_combo_box_remeasure            (GtkComboBox      *combo_box)
 
 static void     gtk_combo_box_unset_model          (GtkComboBox      *combo_box);
 
-static void     gtk_combo_box_size_request         (GtkWidget        *widget,
-                                                    GtkRequisition   *requisition);
 static void     gtk_combo_box_size_allocate        (GtkWidget        *widget,
                                                     GtkAllocation    *allocation);
 static void     gtk_combo_box_forall               (GtkContainer     *container,
@@ -463,8 +463,13 @@ static void     gtk_combo_box_buildable_custom_tag_end       (GtkBuildable  *bui
                                                              gpointer      *data);
 
 /* GtkCellEditable method implementations */
-static void gtk_combo_box_start_editing (GtkCellEditable *cell_editable,
-                                        GdkEvent        *event);
+static void     gtk_combo_box_start_editing                  (GtkCellEditable *cell_editable,
+                                                             GdkEvent        *event);
+
+static void     gtk_combo_box_extended_layout_init           (GtkExtendedLayoutIface *iface);
+static void     gtk_combo_box_get_desired_size               (GtkExtendedLayout      *layout,
+                                                             GtkRequisition         *minimum_size,
+                                                             GtkRequisition         *natural_size);
 
 
 G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
@@ -473,7 +478,9 @@ G_DEFINE_TYPE_WITH_CODE (GtkComboBox, gtk_combo_box, GTK_TYPE_BIN,
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_EDITABLE,
                                                gtk_combo_box_cell_editable_init)
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
-                                               gtk_combo_box_buildable_init))
+                                               gtk_combo_box_buildable_init)
+                        G_IMPLEMENT_INTERFACE (GTK_TYPE_EXTENDED_LAYOUT,
+                                               gtk_combo_box_extended_layout_init))
 
 
 /* common */
@@ -495,7 +502,6 @@ gtk_combo_box_class_init (GtkComboBoxClass *klass)
 
   widget_class = (GtkWidgetClass *)klass;
   widget_class->size_allocate = gtk_combo_box_size_allocate;
-  widget_class->size_request = gtk_combo_box_size_request;
   widget_class->expose_event = gtk_combo_box_expose_event;
   widget_class->scroll_event = gtk_combo_box_scroll_event;
   widget_class->mnemonic_activate = gtk_combo_box_mnemonic_activate;
@@ -932,8 +938,9 @@ gtk_combo_box_init (GtkComboBox *combo_box)
   GTK_BIN (combo_box)->child = priv->cell_view;
   gtk_widget_show (priv->cell_view);
 
-  priv->width = 0;
-  priv->height = 0;
+  memset (&priv->minimum_size, 0x0, sizeof (GtkRequisition));
+  memset (&priv->natural_size, 0x0, sizeof (GtkRequisition));
+
   priv->wrap_width = 0;
 
   priv->active = -1;
@@ -2047,164 +2054,6 @@ gtk_combo_box_calc_requested_width (GtkComboBox *combo_box,
   return req.width + padding;
 }
 
-static void
-gtk_combo_box_remeasure (GtkComboBox *combo_box)
-{
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  GtkTreeIter iter;
-  GtkTreePath *path;
-
-  if (!priv->model ||
-      !gtk_tree_model_get_iter_first (priv->model, &iter))
-    return;
-
-  priv->width = 0;
-  priv->height = 0;
-
-  path = gtk_tree_path_new_from_indices (0, -1);
-
-  do
-    {
-      GtkRequisition req;
-
-      if (priv->cell_view)
-       gtk_cell_view_get_size_of_row (GTK_CELL_VIEW (priv->cell_view), 
-                                       path, &req);
-      else
-        {
-          req.width = 0;
-          req.height = 0;
-        }
-
-      priv->width = MAX (priv->width, req.width);
-      priv->height = MAX (priv->height, req.height);
-
-      gtk_tree_path_next (path);
-    }
-  while (gtk_tree_model_iter_next (priv->model, &iter));
-
-  gtk_tree_path_free (path);
-}
-
-static void
-gtk_combo_box_size_request (GtkWidget      *widget,
-                            GtkRequisition *requisition)
-{
-  gint width, height;
-  gint focus_width, focus_pad;
-  gint font_size;
-  gint arrow_size;
-  GtkRequisition bin_req;
-  PangoContext *context;
-  PangoFontMetrics *metrics;
-  PangoFontDescription *font_desc;
-
-  GtkComboBox *combo_box = GTK_COMBO_BOX (widget);
-  GtkComboBoxPrivate *priv = combo_box->priv;
-  /* common */
-  gtk_widget_size_request (GTK_BIN (widget)->child, &bin_req);
-  gtk_combo_box_remeasure (combo_box);
-  bin_req.width = MAX (bin_req.width, priv->width);
-  bin_req.height = MAX (bin_req.height, priv->height);
-
-  gtk_widget_style_get (GTK_WIDGET (widget),
-                       "focus-line-width", &focus_width,
-                       "focus-padding", &focus_pad,
-                       "arrow-size", &arrow_size,
-                       NULL);
-
-  font_desc = GTK_BIN (widget)->child->style->font_desc;
-  context = gtk_widget_get_pango_context (widget);
-  metrics = pango_context_get_metrics (context, font_desc,
-                                      pango_context_get_language (context));
-  font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
-                           pango_font_metrics_get_descent (metrics));
-  pango_font_metrics_unref (metrics);
-
-  arrow_size = MAX (arrow_size, font_size);
-
-  gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
-
-  if (!priv->tree_view)
-    {
-      /* menu mode */
-
-      if (priv->cell_view)
-        {
-          GtkRequisition button_req, sep_req, arrow_req;
-          gint border_width, xthickness, ythickness;
-
-          gtk_widget_size_request (priv->button, &button_req);
-         border_width = GTK_CONTAINER (combo_box)->border_width;
-          xthickness = priv->button->style->xthickness;
-          ythickness = priv->button->style->ythickness;
-
-          bin_req.width = MAX (bin_req.width, priv->width);
-          bin_req.height = MAX (bin_req.height, priv->height);
-
-          gtk_widget_size_request (priv->separator, &sep_req);
-          gtk_widget_size_request (priv->arrow, &arrow_req);
-
-          height = MAX (sep_req.height, arrow_req.height);
-          height = MAX (height, bin_req.height);
-
-          width = bin_req.width + sep_req.width + arrow_req.width;
-
-          height += 2*(border_width + ythickness + focus_width + focus_pad);
-          width  += 2*(border_width + xthickness + focus_width + focus_pad);
-
-          requisition->width = width;
-          requisition->height = height;
-        }
-      else
-        {
-          GtkRequisition but_req;
-
-          gtk_widget_size_request (priv->button, &but_req);
-
-          requisition->width = bin_req.width + but_req.width;
-          requisition->height = MAX (bin_req.height, but_req.height);
-        }
-    }
-  else
-    {
-      /* list mode */
-      GtkRequisition button_req, frame_req;
-
-      /* sample + frame */
-      *requisition = bin_req;
-
-      requisition->width += 2 * focus_width;
-      
-      if (priv->cell_view_frame)
-        {
-         gtk_widget_size_request (priv->cell_view_frame, &frame_req);
-         if (priv->has_frame)
-           {
-             requisition->width += 2 *
-               (GTK_CONTAINER (priv->cell_view_frame)->border_width +
-                GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
-             requisition->height += 2 *
-               (GTK_CONTAINER (priv->cell_view_frame)->border_width +
-                GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
-           }
-        }
-
-      /* the button */
-      gtk_widget_size_request (priv->button, &button_req);
-
-      requisition->height = MAX (requisition->height, button_req.height);
-      requisition->width += button_req.width;
-    }
-
-  if (GTK_SHADOW_NONE != priv->shadow_type)
-    {
-      requisition->height += 2 * widget->style->ythickness;
-      requisition->width += 2 * widget->style->xthickness;
-    }
-}
-
 #define GTK_COMBO_BOX_SIZE_ALLOCATE_BUTTON                                     \
   gtk_widget_size_request (combo_box->priv->button, &req);                     \
                                                                                \
@@ -3655,14 +3504,14 @@ gtk_combo_box_menu_row_changed (GtkTreeModel *model,
 
   width = gtk_combo_box_calc_requested_width (combo_box, path);
 
-  if (width > priv->width)
+  if (width > priv->minimum_size.width)
     {
       if (priv->cell_view)
        {
          gtk_widget_set_size_request (priv->cell_view, width, -1);
          gtk_widget_queue_resize (priv->cell_view);
        }
-      priv->width = width;
+      priv->minimum_size.width = width;
     }
 }
 
@@ -4168,14 +4017,14 @@ gtk_combo_box_list_row_changed (GtkTreeModel *model,
 
   width = gtk_combo_box_calc_requested_width (combo_box, path);
 
-  if (width > priv->width)
+  if (width > priv->minimum_size.width)
     {
       if (priv->cell_view) 
        {
          gtk_widget_set_size_request (priv->cell_view, width, -1);
          gtk_widget_queue_resize (priv->cell_view);
        }
-      priv->width = width;
+      priv->minimum_size.width = width;
     }
 }
 
@@ -4227,8 +4076,11 @@ gtk_combo_box_cell_layout_pack_start (GtkCellLayout   *layout,
   priv->cells = g_slist_append (priv->cells, info);
 
   if (priv->cell_view)
-    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
-                                cell, expand);
+    {
+      gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (priv->cell_view),
+                                 cell, expand);
+
+    }
 
   if (priv->column)
     gtk_tree_view_column_pack_start (priv->column, cell, expand);
@@ -5962,5 +5814,197 @@ gtk_combo_box_buildable_custom_tag_end (GtkBuildable *buildable,
                                            data);
 }
 
+
+
+
+static void
+gtk_combo_box_extended_layout_init (GtkExtendedLayoutIface *iface)
+{
+  iface->get_desired_size = gtk_combo_box_get_desired_size;
+}
+
+static void
+gtk_combo_box_remeasure (GtkComboBox *combo_box)
+{
+  GtkComboBoxPrivate *priv = combo_box->priv;
+  GtkTreeIter iter;
+  GtkTreePath *path;
+
+  if (!priv->model ||
+      !gtk_tree_model_get_iter_first (priv->model, &iter))
+    return;
+
+  memset (&priv->minimum_size, 0x0, sizeof (GtkRequisition));
+  memset (&priv->natural_size, 0x0, sizeof (GtkRequisition));
+
+  path = gtk_tree_path_new_from_indices (0, -1);
+
+  do
+    {
+      GtkRequisition req, nat_req;
+
+      if (priv->cell_view)
+       gtk_cell_view_get_desired_size_of_row (GTK_CELL_VIEW (priv->cell_view), 
+                                              path, &req, &nat_req);
+      else
+        {
+         memset (&req, 0x0, sizeof (GtkRequisition));
+         memset (&nat_req, 0x0, sizeof (GtkRequisition));
+        }
+
+      priv->minimum_size.width  = MAX (priv->minimum_size.width,  req.width);
+      priv->minimum_size.height = MAX (priv->minimum_size.height, req.height);
+
+      priv->natural_size.width  = MAX (priv->natural_size.width,  nat_req.width);
+      priv->natural_size.height = MAX (priv->natural_size.height, nat_req.height);
+
+      gtk_tree_path_next (path);
+    }
+  while (gtk_tree_model_iter_next (priv->model, &iter));
+
+  gtk_tree_path_free (path);
+}
+
+static void
+gtk_combo_box_get_desired_size (GtkExtendedLayout *layout,
+                               GtkRequisition    *minimum_size,
+                               GtkRequisition    *natural_size)
+{
+  GtkComboBox           *combo_box = GTK_COMBO_BOX (layout);
+  GtkComboBoxPrivate    *priv = combo_box->priv;
+  gint                   focus_width, focus_pad;
+  gint                   font_size, arrow_size;
+  GtkRequisition         bin_req, bin_nat_req;
+  PangoContext          *context;
+  PangoFontMetrics      *metrics;
+  PangoFontDescription  *font_desc;
+  GtkWidget             *child;
+
+  child = gtk_bin_get_child (GTK_BIN (layout));
+  /* common */
+  gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (child), &bin_req, &bin_nat_req);
+  gtk_combo_box_remeasure (combo_box);
+
+  bin_req.width      = MAX (bin_req.width,      priv->minimum_size.width);
+  bin_req.height     = MAX (bin_req.height,     priv->minimum_size.height);
+  bin_nat_req.width  = MAX (bin_nat_req.width,  priv->natural_size.width);
+  bin_nat_req.height = MAX (bin_nat_req.height, priv->natural_size.height);
+
+  gtk_widget_style_get (GTK_WIDGET (layout),
+                       "focus-line-width", &focus_width,
+                       "focus-padding", &focus_pad,
+                       "arrow-size", &arrow_size,
+                       NULL);
+
+  font_desc = child->style->font_desc;
+  context = gtk_widget_get_pango_context (GTK_WIDGET (layout));
+  metrics = pango_context_get_metrics (context, font_desc,
+                                      pango_context_get_language (context));
+  font_size = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) +
+                           pango_font_metrics_get_descent (metrics));
+  pango_font_metrics_unref (metrics);
+
+  arrow_size = MAX (arrow_size, font_size);
+
+  gtk_widget_set_size_request (priv->arrow, arrow_size, arrow_size);
+
+  if (!priv->tree_view)
+    {
+      /* menu mode */
+         
+      if (priv->cell_view)
+        {
+          GtkRequisition button_req, sep_req, arrow_req;
+          gint border_width, xthickness, ythickness, xpad, ypad;
+
+          gtk_widget_size_request (priv->button, &button_req);
+         border_width = GTK_CONTAINER (combo_box)->border_width;
+          xthickness = priv->button->style->xthickness;
+          ythickness = priv->button->style->ythickness;
+
+         xpad = 2*(border_width + xthickness + focus_width + focus_pad);
+         ypad = 2*(border_width + ythickness + focus_width + focus_pad);
+
+          gtk_widget_size_request (priv->separator, &sep_req);
+          gtk_widget_size_request (priv->arrow, &arrow_req);
+
+          minimum_size->width  = bin_req.width + sep_req.width + arrow_req.width;
+          minimum_size->height = MAX (sep_req.height, arrow_req.height);
+          minimum_size->height = MAX (minimum_size->height, bin_req.height);
+
+          natural_size->width  = bin_nat_req.width + sep_req.width + arrow_req.width;
+          natural_size->height = MAX (minimum_size->height, bin_nat_req.height);
+
+          minimum_size->width  += xpad;
+          minimum_size->height += ypad;
+          natural_size->width  += xpad;
+          natural_size->height += ypad;
+        }
+      else
+        {
+          GtkRequisition but_req, but_nat_req;
+
+          gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (priv->button), 
+                                               &but_req, &but_nat_req);
+
+          minimum_size->width  = bin_req.width + but_req.width;
+          minimum_size->height = MAX (bin_req.height, but_req.height);
+
+          natural_size->width  = bin_nat_req.width + but_nat_req.width;
+          natural_size->height = MAX (bin_nat_req.height, but_nat_req.height);
+        }
+    }
+  else
+    {
+      /* list mode */
+      GtkRequisition button_req, button_nat_req, frame_req;
+
+      /* sample + frame */
+      *minimum_size = bin_req;
+      *natural_size = bin_nat_req;
+
+      minimum_size->width += 2 * focus_width;
+      natural_size->width += 2 * focus_width;
+      
+      if (priv->cell_view_frame)
+        {
+         gtk_widget_size_request (priv->cell_view_frame, &frame_req);
+         if (priv->has_frame)
+           {
+             gint xpad = 2 * (GTK_CONTAINER (priv->cell_view_frame)->border_width +
+                              GTK_WIDGET (priv->cell_view_frame)->style->xthickness);
+             
+             gint ypad = 2 * (GTK_CONTAINER (priv->cell_view_frame)->border_width +
+                              GTK_WIDGET (priv->cell_view_frame)->style->ythickness);
+
+             minimum_size->width  += xpad;
+             minimum_size->height += ypad;
+             natural_size->width  += xpad;
+             natural_size->height += ypad;
+           }
+        }
+
+      /* the button */
+      gtk_extended_layout_get_desired_size (GTK_EXTENDED_LAYOUT (priv->button), 
+                                           &button_req, &button_nat_req);
+
+      minimum_size->width += button_req.width;
+      minimum_size->height = MAX (minimum_size->height, button_req.height);
+
+      natural_size->width += button_nat_req.width;
+      natural_size->height = MAX (natural_size->height, button_nat_req.height);
+    }
+
+  if (GTK_SHADOW_NONE != priv->shadow_type)
+    {
+      minimum_size->width  += 2 * GTK_WIDGET (layout)->style->xthickness;
+      minimum_size->height += 2 * GTK_WIDGET (layout)->style->ythickness;
+
+      natural_size->width  += 2 * GTK_WIDGET (layout)->style->xthickness;
+      natural_size->height += 2 * GTK_WIDGET (layout)->style->ythickness;
+    }
+}
+
 #define __GTK_COMBO_BOX_C__
 #include "gtkaliasdef.c"